// Copyright (c) 2009 Luca Marrocco.
// Modified or written by Luca Marrocco for inclusion with codej.
// Licensed under the Apache License, Version 2.0 (the "License")
package codej.source;

import static codej.language.java.Recognize.*;

import java.util.*;

import codej.ast.*;

/** @author Luca Marrocco */
public class JavaLanguage {

	interface Recognize {
		String from(String source);
	}

	public static List<String> linesOf(String source) {
		List<String> lines = new LinkedList<String>();

		Scanner scanner = new Scanner(source).useDelimiter("\n");

		while (scanner.hasNext())
			lines.add(scanner.next());

		return lines;
	}

	private static Annotation newAnnotation(String statement) {
		Annotation annotation = new Annotation("Test");
		return annotation;
	}

	private static Block newBlock(String statement) {
		Block block = new Block();
		statement = statement.replaceFirst("\\{", "").replaceFirst("\\}$", "");
		if (statement.isEmpty())
			return block;
		block.addStatement((Statement) nodeFor(statement));
		return block;
	}

	private static ClassOrInterfaceTypeDeclaration newClassDeclaration(String statement) {
		ClassOrInterfaceTypeDeclaration typeDeclaration = new ClassOrInterfaceTypeDeclaration();

		typeDeclaration.setIsInterface(false);

		boolean hasPublicModifier = statement.matches(".*public.*");
		boolean hasPrivateModifier = statement.matches(".*private.*");
		boolean hasProtectedModifier = statement.matches(".*protected.*");
		boolean hasAbstractModifier = statement.matches(".*abstract.*");

		if (hasPublicModifier)
			typeDeclaration.addModifier(Modifier.publicModifier);
		if (hasPrivateModifier)
			typeDeclaration.addModifier(Modifier.privateModifier);
		if (hasProtectedModifier)
			typeDeclaration.addModifier(Modifier.protectedModifier);
		if (hasAbstractModifier)
			typeDeclaration.addModifier(Modifier.abstractModifier);

		String name = statement;
		name = name.replaceFirst("class", "");
		name = name.replaceFirst("public", "");
		name = name.replaceFirst("private", "");
		name = name.replaceFirst("protected", "");
		name = name.replaceFirst("abstract", "");
		name = name.replaceAll("\\{\\}$", "");
		name = name.replaceAll(" ", "");
		typeDeclaration.setName(name);

		return typeDeclaration;
	}

	private static CompilationUnit newCompilationUnit(String statement) {
		CompilationUnit compilationUnit = new CompilationUnit();
		compilationUnit.setPackageName(statement.replaceFirst("package *", "").replaceFirst(";", ""));
		return compilationUnit;
	}

	private static Node newDocumentation(String statement) {
		Node node = new AbstractNode() {
			public void accept(Visitor visitor) {
			}
		};

		return node;
	}

	private static Node newForExpression(String statement) {
		Node node = new AbstractNode() {
			public void accept(Visitor visitor) {
			}
		};

		return node;
	}

	private static Node newIfExpression(String statement) {
		Node node = new AbstractNode() {
			public void accept(Visitor visitor) {
			}
		};

		return node;
	}

	private static ImportDeclaration newImportDeclaration(String statement) {
		ImportDeclaration importDeclaration = new ImportDeclaration();

		importDeclaration.setStatic(statement.matches("import *static.*"));

		importDeclaration.setName(statement.replaceFirst("^import *static *", "").replaceFirst("^import ", "").replaceFirst(";", ""));

		return importDeclaration;
	}

	private static ClassOrInterfaceTypeDeclaration newInterfaceDeclaration(String statement) {
		ClassOrInterfaceTypeDeclaration typeDeclaration = new ClassOrInterfaceTypeDeclaration();

		typeDeclaration.setIsInterface(true);

		boolean hasPublicModifier = statement.matches(".*public.*");
		boolean hasPrivateModifier = statement.matches(".*private.*");
		boolean hasProtectedModifier = statement.matches(".*protected.*");

		if (hasPublicModifier)
			typeDeclaration.addModifier(Modifier.publicModifier);
		if (hasPrivateModifier)
			typeDeclaration.addModifier(Modifier.privateModifier);
		if (hasProtectedModifier)
			typeDeclaration.addModifier(Modifier.protectedModifier);

		String name = statement;
		name = name.replaceFirst("interface", "");
		name = name.replaceFirst("public", "");
		name = name.replaceFirst("private", "");
		name = name.replaceFirst("protected", "");
		name = name.replaceAll("\\{\\}$", "");
		name = name.replaceAll(" ", "");
		typeDeclaration.setName(name);

		return typeDeclaration;
	}

	private static Node newSwitchExpression(String statement) {
		return new AbstractNode() {
			public void accept(Visitor visitor) {
			}
		};
	}

	private static Node newWhileExpression(String statement) {
		return new AbstractNode() {
			public void accept(Visitor visitor) {
			}
		};
	}

	public static Node nodeFor(String statement) {
		if (statement.startsWith("import"))
			return newImportDeclaration(statement);
		if (statement.startsWith("package"))
			return newCompilationUnit(statement);
		if (statement.startsWith("public class"))
			return newClassDeclaration(statement);
		if (statement.startsWith("private class"))
			return newClassDeclaration(statement);
		if (statement.startsWith("protected class"))
			return newClassDeclaration(statement);
		if (statement.startsWith("abstract class"))
			return newClassDeclaration(statement);
		if (statement.startsWith("class"))
			return newClassDeclaration(statement);
		if (statement.startsWith("public interface"))
			return newInterfaceDeclaration(statement);
		if (statement.startsWith("private interface"))
			return newInterfaceDeclaration(statement);
		if (statement.startsWith("protected interface"))
			return newInterfaceDeclaration(statement);
		if (statement.startsWith("interface"))
			return newInterfaceDeclaration(statement);
		if (statement.startsWith("{"))
			return newBlock(statement);
		if (statement.startsWith("@"))
			return newAnnotation(statement);
		if (statement.startsWith("//"))
			return newDocumentation(statement);
		if (statement.startsWith("/*"))
			return newDocumentation(statement);
		if (statement.startsWith("/**"))
			return newDocumentation(statement);
		if (statement.startsWith("if"))
			return newIfExpression(statement);
		if (statement.startsWith("while"))
			return newWhileExpression(statement);
		if (statement.startsWith("for"))
			return newForExpression(statement);
		if (statement.startsWith("switch"))
			return newSwitchExpression(statement);

		throw new UnknownSourceException("what is '" + statement + "'?");
	}

	public static List<String> statementsFor(List<String> lines) {
		List<String> statements = new LinkedList<String>();

		for (String line : lines)
			statements.addAll(statementsFor(line));

		return statements;
	}

	public static List<String> statementsFor(String source) {
		List<String> statements = new LinkedList<String>();

		Scanner scanner = new Scanner(source).useDelimiter(";");

		while (scanner.hasNext())
			statements.add(scanner.next());

		return statements;
	}

	private String source;

	public JavaLanguage(String source) {
		this.source = source;
	}

	public Node compile() {
		return recognize(source);
	}

	private MethodDeclaration newStaticMethod(String name, Block block) {
		MethodDeclaration methodDeclaration = new MethodDeclaration();
		methodDeclaration.setType(new Type().setName("void"));
		methodDeclaration.addModifier(Modifier.publicModifier);
		methodDeclaration.addModifier(Modifier.staticModifier);
		methodDeclaration.setName(name);
		methodDeclaration.setBlock(block);
		return methodDeclaration;
	}

	private TypeDeclaration newStaticType(String name, MethodDeclaration staticMethod) {
		ClassOrInterfaceTypeDeclaration classDeclaration = new ClassOrInterfaceTypeDeclaration();
		classDeclaration.setIsInterface(false);
		classDeclaration.addModifier(Modifier.publicModifier);
		classDeclaration.addModifier(Modifier.staticModifier);
		classDeclaration.setName(name);
		classDeclaration.addBodyDeclaration(staticMethod);
		return classDeclaration;
	}
}